package com.cctc.juc.source.Bitc.Glock.dlock.Credis.redission;
/**
 * 【Redission 客户端】
 * Redission 简介：
 * Redission 使用：
 * 1. Redission 客户端配置：单机模式、主从模式、哨兵模式、集群模式。
 * - 熟悉 Redis 四种部署模式底层（数据同步、持久化、高可用）实现原理。
 * - 熟练掌握 Redis 四种部署方式，使用过程。
 * 2. Redission 分布式锁、分布式计数器、... 等分布式数据同步解决方案。
 * 要求：熟悉 Redission 底层实现原理。
 * 1）独占锁：JUC 显式锁中 ReentrantLock 可重入独占锁的 Redission 分布式实现 RLock 是一个标准的分布式锁。
 * --> 通过 Redission 的封装，使用 RLock（同样继承自 Lock 接口）分布式锁的方式几乎和 ReentrantLock 一样，非常简单。
 * --> 新特性：除了独占、公平、可重入、非阻塞抢锁、限时阻塞抢锁、可中断抢锁、手动解锁（基于发布订阅通道唤醒后继节点）外，还支持自动过期解锁，以及异步抢锁（Future 异步抢锁成功后再执行临界区代码块）等，以及分布式系统的高可用、高性能等基本要求。
 * 2）共享锁：JUC 共享锁的 Redission 分布式解决方案，如（可过期）信号量、倒数闩 / 闭锁等；
 * 3）组合锁：读写锁、联（合）锁（MultiLock）、红（过半）锁（RedLock）等等。
 * --> 联（合）锁（MultiLock）、红（过半）锁（RedLock）是为了解决 Redis 分布式锁的高可用问题而生的。
 * （熟悉）Redission 高可用分布式锁算法思想：为了保证分布式锁的高可用，应该在整个 redis 集群上创建联锁或者红锁，保证必须在全部或大多数 redis 节点上都成功创建后，才能算在整个 redis 集群上加锁成功，避免仅仅在一个 redis 实例上加锁而带来的数据安全问题。
 * --> 新特性：同上。
 * 3）JUC 中的 AtomicLong（计数器）、LongAdder（分段计数器）的 Redission 分布式 “替代品”，如 RAtomicLong、LongAdder（12000 倍）等等。
 * 4）参照 LongAdder（分段计数器）、ConcurrentHashMap（分段哈希表）思想实现一个 Redission 分段锁。
 * 3. STW 、网络抖动、客户端宕机等导致的锁自动过期问题。
 * 问题描述：client1 在持锁期间发生了 Full GC，且其 STW 停顿时间超过了锁的过期时间，被 redis 自动释放了，此时 client2 占有了锁，然而 client1 在代码层面仍（假）持有锁，client1 & client2 同时执行临界区代码块，破坏了互斥约束，从而引发线程安全问题。
 * 解决方案：
 * 1）借鉴 CAS 乐观锁思想，（入侵代码）数据层面增加（token、version）版本号；
 * 2）（Redission）使用 watch dog 看门狗（后台线程，非入侵）监控锁 & 自动续期机制。
 * 提示：掌握底层原理。
 * 注意事项：
 * - watch dog 只有在未显式（手动）指定锁的过期时间时（默认为 lockWatchdogTimeout）才会生效（监控锁的过期时间并自动续期）。
 * - watch dog 检查频率（lockWatchdogTimeout / 3）不要设置的太小（例如 100 / 3 = 33 ms），否则有可能由于网络原因，在 watch dog 去续期时，锁已经过期释放了。
 * 总结：
 * 1）如果要使 watch dog 生效，就不要显式（手动）指定锁的过期时间（默认为 lockWatchdogTimeout）；
 * 2）watch dog 会每间隔 lockWatchdogTimeout / 3 时间去执行监视锁的自动续期操作，因此 lockWatchdogTimeout 不要设置的太小，否则有可能由于网络原因，在 watch dog 去续期时，锁已经过期释放了；
 * 3）watch dog 通过类似 Netty 的 Future 功能来异步执行监视锁的自动续期操作；
 * 4）自动续期操作最终是通过 lua 脚本来完成的。
 * 补充知识点：
 * 【Lua 脚本语言】
 * 简介：Lua 是一种开源、简单易学、轻量小巧的脚本语言，使用标准 C 语言编写。其设计的目的就是为了嵌入到应用程序中，从而为应用程序提供灵活的扩展和定制功能。
 * Redis 使用 Lua 脚本可以带来以下优点：
 * 1）原子操作，redis 使用 eval() 函数执行（由一大堆复杂的业务逻辑封装成而成的）lua 代码时，会被当成一个命令去执行，以保证（这段复杂业务逻辑封装成而成的）lua 代码执行的原子性。
 * 2）代码复用，redis 支持缓存客户端发送过来的 lua 脚本，可以通过脚本 id 标识反复执行，重复利用。
 * 3）减少网络开销，将多个 redis 操作合并成一个脚本批量交互，以及通过脚本 id 标识执行 lua 脚本缓存，可以有效减少网络开销。
 */